package chsql

import (
	"fmt"
	"net/url"
	"strings"
)

type ISqlQueryBuilder interface {
	String() string
	WithSeparator(string) ISqlQueryBuilder
	AddParam(ISqlQueryParam) ISqlQueryBuilder
}
type IValueEncoder interface {
	Encode(string) string
}
type ISqlQueryParam interface {
	WithKey(string) ISqlQueryParam
	WithValue(string) ISqlQueryParam
	KeyEncoded(IValueEncoder) ISqlQueryParam
	ValueEncoded(IValueEncoder) ISqlQueryParam
	WithSeparator(string) ISqlQueryParam
	String() string
}
type BasicParam struct {
	k, v, sep string
}

func NewBasicParam() ISqlQueryParam {
	return &BasicParam{
		sep: "=",
	}
}
func NewBasicParamKV(k, v string) ISqlQueryParam {
	return &BasicParam{
		k:   k,
		v:   v,
		sep: "=",
	}
}
func (b *BasicParam) WithKey(s string) ISqlQueryParam {
	b.k = s
	return b
}

func (b *BasicParam) WithValue(s string) ISqlQueryParam {
	b.v = s
	return b
}

func (b *BasicParam) KeyEncoded(encoder IValueEncoder) ISqlQueryParam {
	if nil == encoder {
		return b
	}
	b.k = encoder.Encode(b.k)
	return b
}

func (b *BasicParam) ValueEncoded(encoder IValueEncoder) ISqlQueryParam {
	if nil == encoder {
		return b
	}
	b.v = encoder.Encode(b.v)
	return b
}

func (b *BasicParam) WithSeparator(s string) ISqlQueryParam {
	b.sep = s
	return b
}

// String if k or v or sep is empty string else contacts them
func (b *BasicParam) String() string {
	if "" == b.k || "" == b.v || "" == b.sep {
		return ""
	}
	return fmt.Sprintf("%s%s%s", b.k, b.sep, b.v)
}

type BasicBuilder struct {
	sep    string
	params []ISqlQueryParam
}

func NewBasicBuilder() ISqlQueryBuilder {
	return &BasicBuilder{
		sep: "&",
	}
}

// String skip the nil item
func (b *BasicBuilder) String() string {
	if len(b.params) == 0 {
		return ""
	}
	var sb strings.Builder
	for i := 0; i < len(b.params); i++ {
		if nil == b.params[i] {
			continue
		}
		sb.WriteString(b.params[i].String())
		if i < len(b.params)-1 {
			sb.WriteString(b.sep)
		}
	}
	return sb.String()
}

func (b *BasicBuilder) WithSeparator(s string) ISqlQueryBuilder {
	b.sep = s
	return b
}

func (b *BasicBuilder) AddParam(param ISqlQueryParam) ISqlQueryBuilder {
	b.params = append(b.params, param)
	return b
}

type QueryEscapeEncoder struct {
}

func (b *QueryEscapeEncoder) Encode(s string) string {
	return url.QueryEscape(s)
}

var QEscapeEncoder = &QueryEscapeEncoder{}
